<?php

const HOST = 'db';
const DB = 'test_task';
const USER = 'root';
const PASSWORD = 'root_secret';

$mysql = new mysqli(HOST, USER, PASSWORD, DB);

if ($mysql->connect_error) {
    die("Ошибка подключения: " . $mysql->connect_error);
}

$args = array_filter($argv, fn ($item) => !str_contains($item, 'php'));
$entry_token = reset($args);
assert(is_string($entry_token), "Входная строка не предоставлена");
$entry_codes = str_split($entry_token);

validate_entry_codes($entry_codes);

$combinations = get_all_combinations($entry_codes);
echo "Всего комбинаций: " . count($combinations) . PHP_EOL;
file_put_contents('result.json', json_encode($combinations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

/**
 * @param array<string> $entry_codes
 */
function get_all_combinations(array $entry_codes): array
{
    $types = codes_to_ingredients($entry_codes);
    $combos = [[]];
    foreach ($types as $ingrs) {
        $_tmp = array();
        foreach ($combos as $combo) {
            foreach ($ingrs as $ingr) {
                $_tmp[] = array_merge($combo, array($ingr));
            }
        }
        $combos = $_tmp;
    }

    // фильтруем от комбинаций с одинаковыми продуктами
    $combos = array_filter($combos, function (array $combo) {
        $values_count = array_count_values(array_column($combo, 'id'));
        foreach ($combo as $ingr) {
            if ($values_count[$ingr['id']] > 1) {
                return false;
            }
        }
        return true;
    });

    // фильтруем от одинаковых комбинаций
    $combos_id_sum = array_map(function (array $combo) {
        $a = array_column($combo, 'id');
        sort($a);
        return join('', $a);
    }, $combos);
    $filter_keys = array_keys(array_unique($combos_id_sum));
    $combos = array_filter($combos, fn (int $key) => in_array($key, $filter_keys), ARRAY_FILTER_USE_KEY);

    // преоброзоваем в нужный формат
    $combos = array_map(function (array $combo) {
        $res = [];
        $price = 0;
        foreach ($combo as $ingr) {
            $res['products'][] = [
                'id' => $ingr['id'],
                'type' => $ingr['type'],
                'value' => $ingr['value'],
            ];
            $price += $ingr['price'];
        }
        $res['price'] = $price;
        return $res;
    }, $combos);

    return $combos;
}

/**
 * @param array<string> $codes
 * @return array<array<int, array<string, string | int>>>>
 */
function codes_to_ingredients(array $codes): array
{
    global $mysql;
    $stmt = $mysql->prepare(
        sprintf(
            'SELECT i.id as `id`, it.title as `type`, i.price as `price`, i.title as `value`, it.code as `code` FROM ingredient_type it JOIN ingredient i ON it.id = i.type_id WHERE it.code IN (%s);',
            implode(', ', array_fill(0, count($codes), '?'))
        )
    );
    if ($stmt === false) {
        die("Prepare failed: " . $mysql->error);
    }
    $stmt->bind_param(str_repeat('s', count($codes)), ...$codes);
    $stmt->execute();
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        $types[$row['code']][] = [
            'id' => $row['id'],
            'type' => $row['type'],
            'price' => $row['price'],
            'value' => $row['value'],
        ];
    }

    $prepared_types = [];

    foreach ($codes as $code) {
        $prepared_types[] = &$types[$code];
    }

    return $prepared_types;
}

function validate_entry_codes(array $entry_codes): void
{
    $difference = array_diff($entry_codes, get_all_system_codes());

    assert(
        count($difference) === 0,
        sprintf('Входные коды не соответствуют системным: %s', join(', ', $difference))
    );
}

/**
 * @return array<string>
 */
function get_all_system_codes(): array
{
    global $mysql;
    $res = $mysql->query("SELECT code FROM ingredient_type");

    return array_map(
        fn (array $item) => reset($item),
        $res->fetch_all()
    );
}
